package com.skt.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.skt.domain.DTO.UserQueryDTO;
import com.skt.domain.DTO.request.CreateCategoryRequest;
import com.skt.domain.DTO.request.OrderQueryRequest;
import com.skt.domain.DTO.request.UpdateCategoryRequest;
import com.skt.domain.ENUM.AccountStatus;
import com.skt.domain.ENUM.RentalOrderStatus;
import com.skt.domain.ENUM.UserRole;
import com.skt.domain.ENUM.UserRoleStatus;
import com.skt.domain.PO.Category;
import com.skt.domain.PO.RentalOrder;
import com.skt.domain.PO.User;
import com.skt.domain.VO.AdminMerchantApprovalVO;
import com.skt.domain.VO.AdminOrderListVO;
import com.skt.domain.VO.CategoryVO;
import com.skt.domain.VO.UserVO;
import com.skt.mapper.CategoryMapper;
import com.skt.mapper.RentalOrderMapper;
import com.skt.mapper.UserMapper;
import com.skt.result.PageResult;
import com.skt.security.LoginUser;
import com.skt.service.AdminService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class AdminServiceImpl implements AdminService {

    private final UserMapper userMapper;

    private final CategoryMapper categoryMapper;

    private final RentalOrderMapper rentalOrderMapper;

    private final ApplicationEventPublisher eventPublisher; // 用于发布事件（可选）


    /**
     * 审核商家注册申请
     *
     * @param id 商家用户ID
     * @return 审核通过后的商家信息 VO
     */
    @Override
    @Transactional
    public AdminMerchantApprovalVO approveMerchant(Long id) {
        // 1. 身份与权限校验
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new RuntimeException("用户未登录");
        }

        Object principal = authentication.getPrincipal();
        Long currentAdminId;
        if (principal instanceof LoginUser loginUser) {
            currentAdminId = loginUser.getUserId();
        } else {
            throw new RuntimeException("用户不存在");
        }

        if (currentAdminId == null) {
            throw new RuntimeException("登录凭证失效");
        }

        User admin = userMapper.selectById(currentAdminId);
        // 1.null判断 2.账户状态禁用判断 3.账户角色管理员判断
        if (admin == null ||
                admin.getAccountStatus() != AccountStatus.ENABLED ||
                admin.getRole() != UserRole.ADMIN) {
            throw new RuntimeException("权限不足或账户已被禁用");
        }

        // 2. 参数与目标商家校验
        if (id == null || id <= 0) {
            throw new RuntimeException("商家ID无效");
        }

        User merchant = userMapper.selectById(id);
        if (merchant == null) {
            throw new RuntimeException("商家不存在");
        }

        if (merchant.getRole() != UserRole.MERCHANT) {
            throw new RuntimeException("目标用户非商家");
        }

        if (merchant.getRoleStatus() != UserRoleStatus.PENDING) {
            throw new RuntimeException("商家状态不可审核");
        }

        // 3. 执行审核操作（带乐观锁）
        LocalDateTime now = LocalDateTime.now();
        int updated = userMapper.updateMerchantStatusToApproved(
                id,
                UserRoleStatus.PENDING.getValue(), //
                UserRoleStatus.APPROVED.getValue(),
                now,
                currentAdminId
        );

        if (updated == 0) {
            throw new RuntimeException("审核冲突，请刷新后重试");
        }

        // 4. 重新查询最新数据（避免脏读）
        User approvedMerchant = userMapper.selectById(id);

        // 5. 构建安全返回对象
        User m = approvedMerchant;
        AdminMerchantApprovalVO vo = AdminMerchantApprovalVO.builder()
                .id(m.getId())
                .username(m.getUsername())
                .realName(m.getRealName())
                .shopName(m.getShopName())
                .phone(maskPhone(m.getPhone())) // 可选脱敏
                .email(m.getEmail())
                .status(m.getRoleStatus().getValue()) // ✅ Integer，匹配 VO
                .approvedAt(m.getApprovedAt())
                .approvedBy(m.getApprovedBy())
                .createdAt(m.getCreatedAt())
                .updatedAt(m.getUpdatedAt())
                .build();

        // 6. 注册事务提交后的回调（异步执行审核后逻辑）
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 异步执行后续逻辑（如需真正异步，可在此调用 @Async 方法）
                    executePostApprovalActions(approvedMerchant, currentAdminId);
                }
            });
        } else {
            // 非事务环境（测试或异常情况），直接执行（不推荐生产）
            executePostApprovalActions(approvedMerchant, currentAdminId);
        }

        return vo;
    }

    /**
     * 拒绝商家注册申请
     * @param id
     * @param rejectionReason
     * @return
     */
    @Transactional
    @Override
    public AdminMerchantApprovalVO rejectMerchant(Long id, String rejectionReason) {
        // 1.身份与权限校验
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new RuntimeException("用户未登录");
        }

        Object principal = authentication.getPrincipal();
        Long currentAdminId;

        if (principal instanceof LoginUser loginUser) {
            currentAdminId = loginUser.getUserId();
        } else {
            throw new RuntimeException("用户不存在");
        }

        if (currentAdminId == null) {
            throw new RuntimeException("用户凭证失效");
        }

        User admin = userMapper.selectById(currentAdminId);

        if (admin.getRole() != UserRole.ADMIN) {
            throw new RuntimeException("用户不是管理员");
        }

        if (admin.getAccountStatus() != AccountStatus.ENABLED) {
            throw new RuntimeException("用户已被禁用");
        }
        // 2.参数与目标商家校验
        if (id == null || id <= 0) {
            throw new RuntimeException("用户ID无效");
        }

        User merchant = userMapper.selectById(id);

        if (merchant == null) {
            throw new RuntimeException("商家不存在");
        }

        if (merchant.getRole() != UserRole.MERCHANT) {
            throw new RuntimeException("目标用户非商家");
        }

        if (merchant.getRoleStatus() != UserRoleStatus.PENDING) {
            throw new RuntimeException("商家状态不可审核");
        }
        // 3.执行拒绝操作(事物内)
        // TODO rejectionReason 对拒绝原因进行内容校验(文明方面)
        LocalDateTime now = LocalDateTime.now();
        int updated = userMapper.updateMerchantStatusToReject(
                id,
                UserRoleStatus.PENDING.getValue(),
                UserRoleStatus.REJECTED.getValue(),
                now,
                currentAdminId,
                rejectionReason
        );

        if (updated == 0) {
            throw new RuntimeException("审核冲突，请刷新后重试");
        }

        // TODO 发送含拒绝原因的通知（邮件/短信/站内信）；

        // 4. 重新查询最新数据（避免脏读）
        User rejectMerchant = userMapper.selectById(id);

        // 5.构建安全返回对象
        User m = rejectMerchant;
        AdminMerchantApprovalVO vo = AdminMerchantApprovalVO.builder()
                .id(m.getId())
                .username(m.getUsername())
                .realName(m.getRealName())
                .shopName(m.getShopName())
                .phone(maskPhone(m.getPhone())) // 可选脱敏
                .email(m.getEmail())
                .status(m.getRoleStatus().getValue()) // ✅ Integer，匹配 VO
                .rejectedAt(m.getRejectedAt())
                .rejectedBy(m.getRejectedBy())
                .createdAt(m.getCreatedAt())
                .updatedAt(m.getUpdatedAt())
                .build();

        // 6. 注册事务提交后的回调（异步执行审核后逻辑）
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 异步执行后续逻辑（如需真正异步，可在此调用 @Async 方法）
                    executePostRejectActions(rejectMerchant, currentAdminId, rejectionReason);
                }
            });
        } else {
            // 非事务环境（测试或异常情况），直接执行（不推荐生产）
            executePostRejectActions(rejectMerchant, currentAdminId, rejectionReason);
        }

        return vo;
    }


    /**
     * 查询服装分类列表
     * @param name
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public PageResult<CategoryVO> listCategories(String name, Integer pageNum, Integer pageSize) {
        // ========== 1. 身份与权限校验 ==========
        requireAdmin();
        // ========== 2. 参数校验与默认值处理 ==========
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        pageSize = (pageSize == null || pageSize < 1) ? 10 : Math.min(pageSize, 100);

        // ========== 3. 分页查询（MyBatis-Plus）==========
        Page<Category> page = new Page<>(pageNum, pageSize);
        Page<Category> resultPage = categoryMapper.selectListWithParentName(page, name);

        // ========== 4. 转换为 VO 列表 ==========
        List<CategoryVO> voList = resultPage.getRecords().stream().map(category -> {
            CategoryVO vo = new CategoryVO();
            vo.setId(category.getId());
            vo.setName(category.getName());
            vo.setParentId(category.getParentId());
            vo.setParentName(category.getParentName()); // 来自 SQL JOIN
            vo.setSortOrder(category.getSortOrder());
            vo.setStatus(category.getStatus()); // Byte → Byte，直接赋值
            vo.setCreatedAt(category.getCreatedAt());
            return vo;
        }).collect(Collectors.toList());

        // ========== 5. 构建并返回分页结果（兼容现有 PageResult 结构）==========
        return PageResult.success(
                resultPage.getCurrent(),   // 当前页码（long）
                resultPage.getSize(),      // 每页大小（long）
                resultPage.getTotal(),     // 总记录数（long）
                voList                     // 数据列表
        );
    }


    /**
     * 新增服装分类
     * @param request
     * @return
     */
    @Override
    @Transactional
    public CategoryVO createCategory(CreateCategoryRequest request) {
        // ========== 1. 身份与权限校验（复用 approveMerchant 中的逻辑） ==========
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new RuntimeException("用户未登录");
        }

        Object principal = authentication.getPrincipal();
        Long currentAdminId;
        if (principal instanceof LoginUser loginUser) {
            currentAdminId = loginUser.getUserId();
        } else {
            throw new RuntimeException("用户不存在");
        }

        if (currentAdminId == null) {
            throw new RuntimeException("登录凭证失效");
        }

        User admin = userMapper.selectById(currentAdminId);
        if (admin == null ||
                admin.getAccountStatus() != AccountStatus.ENABLED ||
                admin.getRole() != UserRole.ADMIN) {
            throw new RuntimeException("权限不足或账户已被禁用");
        }

        // ========== 2. 参数校验 ==========
        String categoryName = request.getName().trim();
        if (categoryName.length() > 50) {
            throw new RuntimeException("分类名称长度不能超过50个字符");
        }

        Long parentId = request.getParentId();
        Integer sortOrder = request.getSortOrder();
        if (sortOrder < 0) {
            throw new RuntimeException("排序值必须为非负整数");
        }

        // ========== 3. 父分类校验 ==========
        if (!Objects.equals(parentId, 0L)) {
            Category parent = categoryMapper.selectById(parentId);
            if (parent == null || parent.getDeleted() != 0) {
                throw new RuntimeException("父分类不存在或已被删除");
            }
        }

        // ========== 4. 同级分类名称唯一校验 ==========
        LambdaQueryWrapper<Category> duplicateCheck = new LambdaQueryWrapper<Category>()
                .eq(Category::getParentId, parentId)
                .eq(Category::getName, categoryName)
                .eq(Category::getDeleted, (byte) 0);
        if (categoryMapper.exists(duplicateCheck)) {
            throw new RuntimeException("同级分类中已存在名称为“" + categoryName + "”的分类");
        }

        // ========== 5. 插入新分类 ==========
        Category category = new Category()
                .setName(categoryName)
                .setParentId(parentId)
                .setSortOrder(sortOrder)
                .setStatus((byte) 1)
                .setCreatedBy(currentAdminId)
                .setUpdatedBy(currentAdminId)
                .setDeleted((byte) 0);

        categoryMapper.insert(category);
        Long savedId = category.getId();

        // ========== 6. 重新查询最新数据 ==========
        Category saved = categoryMapper.selectById(savedId);

        // ========== 7. 转换为 VO ==========
        CategoryVO vo = new CategoryVO();
        vo.setId(saved.getId());
        vo.setName(saved.getName());
        vo.setParentId(saved.getParentId());
        vo.setSortOrder(saved.getSortOrder());
        vo.setStatus(saved.getStatus());
        vo.setCreatedAt(saved.getCreatedAt());

        if (!Objects.equals(saved.getParentId(), 0L)) {
            Category parent = categoryMapper.selectById(saved.getParentId());
            vo.setParentName(parent != null ? parent.getName() : null);
        }

        // ========== 8. 事务提交后记录日志（IP 暂用占位符） ==========
        String ip = "0.0.0.0"; // TODO：后续从 HttpServletRequest 获取
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                log.info("管理员 {} 新增服装分类 \"{}\"，ID={}，IP={}",
                        currentAdminId, categoryName, savedId, ip);
            }
        });

        return vo;
    }


    /**
     * 更新服装分类
     *
     * @param id      分类ID（路径参数）
     * @param request 更新请求体
     * @return 更新后的分类 VO
     */
    @Override
    @Transactional
    public CategoryVO updateCategory(Long id, UpdateCategoryRequest request) {
        // 1. 身份与权限校验
        Long currentAdminId = requireAdmin();

        // 2. 参数与目标校验
        if (id == null || id <= 0) {
            throw new RuntimeException("服装分类ID无效");
        }

        // 查询目标分类（需检查是否存在且未删除）
        Category existing = categoryMapper.selectById(id);
        if (existing == null || existing.getDeleted() != 0) {
            throw new RuntimeException("分类不存在");
        }

        String newName = request.getName().trim();
        Long newParentId = request.getParentId();
        Integer newSortOrder = request.getSortOrder();

        // 3. 业务规则校验

        // 3.1 父分类合法性校验（若 parentId != 0）
        if (!Objects.equals(newParentId, 0L)) {
            Category parent = categoryMapper.selectById(newParentId);
            if (parent == null || parent.getDeleted() != 0) {
                throw new RuntimeException("父分类不存在或已被删除");
            }
        }

        // 3.2 防止父子循环：不能将分类的父级设为自己或其后代
        if (isDescendantOrSelf(id, newParentId)) {
            throw new RuntimeException("不能将分类的父级设置为自己或其子分类");
        }

        // 3.3 同级名称唯一性校验（仅当名称发生变化时）
        if (!existing.getName().equals(newName)) {
            LambdaQueryWrapper<Category> duplicateCheck = new LambdaQueryWrapper<Category>()
                    .eq(Category::getParentId, newParentId)
                    .eq(Category::getName, newName)
                    .eq(Category::getDeleted, (byte) 0);
            if (categoryMapper.exists(duplicateCheck)) {
                throw new RuntimeException("同级分类中已存在名称为“" + newName + "”的分类");
            }
        }

        // 4. 执行更新操作（事务内）
        Category updateObj = new Category();
        updateObj.setId(id);
        updateObj.setName(newName);
        updateObj.setParentId(newParentId);
        updateObj.setSortOrder(newSortOrder);
        updateObj.setUpdatedBy(currentAdminId);
        // 注意：不更新 status、deleted 等字段，除非业务需要

        // 使用 MyBatis-Plus 的 updateById，但需确保只更新未删除的记录
        // 实际上，MP 的 updateById 默认无 deleted 条件，因此我们手动加条件更新
        // 更安全的方式：使用 update(entity, wrapper)
        LambdaQueryWrapper<Category> updateWrapper = new LambdaQueryWrapper<Category>()
                .eq(Category::getId, id)
                .eq(Category::getDeleted, (byte) 0);

        int updatedRows = categoryMapper.update(updateObj, updateWrapper);
        if (updatedRows == 0) {
            throw new RuntimeException("分类不存在或已被修改");
        }

        // 5. 重新查询最新数据（确保获取完整字段，如 parentName）
        Category updated = categoryMapper.selectById(id);
        if (updated == null) {
            throw new RuntimeException("更新后无法查询到分类");
        }

        // 6. 转换为 VO
        CategoryVO vo = new CategoryVO();
        vo.setId(updated.getId());
        vo.setName(updated.getName());
        vo.setParentId(updated.getParentId());
        vo.setSortOrder(updated.getSortOrder());
        vo.setStatus(updated.getStatus());
        vo.setCreatedAt(updated.getCreatedAt());
        vo.setUpdatedAt(updated.getUpdatedAt());

        // 设置 parentName（若非顶级）
        if (!Objects.equals(updated.getParentId(), 0L)) {
            Category parent = categoryMapper.selectById(updated.getParentId());
            vo.setParentName(parent != null ? parent.getName() : null);
        }

        // 7. 事务提交后记录操作日志
        String finalOldName = existing.getName();
        String finalNewName = newName;
        String ip = getClientIp(); // 你已有此方法

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                log.info("管理员 {} 更新服装分类 \"{} → {}\"，ID={}，IP={}",
                        currentAdminId, finalOldName, finalNewName, id, ip);
                // TODO: 可在此处清除缓存、发布事件等
            }
        });

        return vo;
    }


    /**
     * 删除服装分类（逻辑删除）
     *
     * @param id 分类ID
     */
    @Override
    @Transactional
    public void deleteCategory(Long id) {
        // 1. 身份与权限校验
        Long currentAdminId = requireAdmin();

        // 2. 参数校验
        if (id == null || id <= 0) {
            throw new RuntimeException("分类ID无效");
        }

        // 3. 查询目标分类
        Category category = categoryMapper.selectById(id);
        if (category == null || category.getDeleted() != 0) {
            throw new RuntimeException("分类不存在");
        }

        // 4. 校验是否存在子分类
        LambdaQueryWrapper<Category> childWrapper = new LambdaQueryWrapper<Category>()
                .eq(Category::getParentId, id)
                .eq(Category::getDeleted, (byte) 0);
        if (categoryMapper.exists(childWrapper)) {
            throw new RuntimeException("请先删除子分类");
        }

        // 6. 执行逻辑删除（条件更新，防止并发）
        Category updateObj = new Category();
        updateObj.setDeleted((byte) 1); // 或 true，根据你的 deleted 字段类型
        updateObj.setDeletedBy(currentAdminId);
        updateObj.setDeletedAt(LocalDateTime.now());

        LambdaQueryWrapper<Category> deleteWrapper = new LambdaQueryWrapper<Category>()
                .eq(Category::getId, id)
                .eq(Category::getDeleted, (byte) 0); // 确保只删除未删除的记录

        int updatedRows = categoryMapper.update(updateObj, deleteWrapper);
        if (updatedRows == 0) {
            throw new RuntimeException("分类已被删除或不存在");
        }

        // 7. 事务提交后记录操作日志（异步）
        String categoryName = category.getName();
        String ip = getClientIp();

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                log.info("管理员 {} 删除服装分类 \"{}\"，ID={}，IP={}",
                        currentAdminId, categoryName, id, ip);
                // 可选：发布事件
                // eventPublisher.publishEvent(new CategoryDeletedEvent(id, currentAdminId));
            }
        });
    }


    /**
     * 查看平台用户列表
     * @param queryDTO
     * @return
     */
    @Override
    public PageResult<UserVO> getUserList(UserQueryDTO queryDTO) {
        // 1. 身份与权限校验
        Long currentAdminId = requireAdmin();
        User admin = userMapper.selectById(currentAdminId);
        if (admin == null ||
                admin.getAccountStatus() != AccountStatus.ENABLED ||
                admin.getRole() != UserRole.ADMIN) {
            throw new RuntimeException("权限不足或账户已被禁用");
        }

        // 2. 参数校验与分页处理
        Integer pageNum = queryDTO.getPageNum() != null ? queryDTO.getPageNum() : 1;
        Integer pageSize = queryDTO.getPageSize() != null ? queryDTO.getPageSize() : 10;

        if (pageNum < 1) pageNum = 1;
        if (pageSize < 1 || pageSize > 100) pageSize = 10;

        // 3. 构建查询条件
        Page<User> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

        // 关键词模糊查询（用户名/手机号/邮箱）
        if (StringUtils.isNotBlank(queryDTO.getKeyword())) {
            String keyword = "%" + queryDTO.getKeyword() + "%";
            wrapper.and(q -> q.like(User::getUsername, keyword)
                    .or().like(User::getPhone, keyword)
                    .or().like(User::getEmail, keyword));
        }

        // 角色过滤
        if (queryDTO.getRole() != null) {
            wrapper.eq(User::getRole, queryDTO.getRole());
        }

        // 状态过滤
        if (queryDTO.getAccountStatus() != null) {
            wrapper.eq(User::getAccountStatus, queryDTO.getAccountStatus());
        }

        // 注册时间范围
        if (queryDTO.getStartDate() != null && queryDTO.getEndDate() != null) {
            wrapper.between(User::getCreatedAt,
                    queryDTO.getStartDate().atStartOfDay(),
                    queryDTO.getEndDate().atTime(23, 59, 59));
        }

        // 排序处理
        if (StringUtils.isNotBlank(queryDTO.getSortBy()) &&
                StringUtils.isNotBlank(queryDTO.getSortOrder())) {

            String sortField = queryDTO.getSortBy();
            boolean isAsc = "asc".equalsIgnoreCase(queryDTO.getSortOrder());

            switch (sortField) {
                case "username":
                    wrapper.orderBy(true, isAsc, User::getUsername);
                    break;
                case "createdAt":
                    wrapper.orderBy(true, isAsc, User::getCreatedAt);
                    break;
                case "updatedAt":
                    wrapper.orderBy(true, isAsc, User::getUpdatedAt);
                    break;
                default:
                    // 默认按注册时间倒序
                    wrapper.orderByDesc(User::getCreatedAt);
            }
        } else {
            // 默认排序
            wrapper.orderByDesc(User::getCreatedAt);
        }

        // 4. 执行分页查询
        Page<User> userPage = userMapper.selectPage(page, wrapper);

        // 5. 数据脱敏与转换
        List<UserVO> voList = userPage.getRecords().stream()
                .map(user -> {
                    UserVO vo = new UserVO();
                    vo.setId(user.getId());
                    vo.setUsername(user.getUsername());
                    vo.setRealName(user.getRealName());

                    // 手机号脱敏
                    vo.setPhone(maskPhone(user.getPhone()));

                    // 邮箱脱敏
                    vo.setEmail(maskEmail(user.getEmail()));

                    // 修复类型转换问题：枚举值转Byte
                    vo.setRole(user.getRole() != null ?
                            user.getRole().getValue().byteValue() : null);

                    vo.setStatus(user.getAccountStatus() != null ?
                            user.getAccountStatus().getValue().byteValue() : null);

                    vo.setCreatedAt(user.getCreatedAt());
                    vo.setUpdatedAt(user.getUpdatedAt());
                    return vo;
                })
                .collect(Collectors.toList());

        // 6. 构建分页结果
        return new PageResult<>(
                userPage.getCurrent(),   // 当前页码
                userPage.getSize(),      // 每页大小
                userPage.getTotal(),     // 总记录数
                voList                   // 数据列表
        );
    }


    /**
     *  查看平台订单总览
     * @param request
     * @return
     */
    @Override
    public PageResult<AdminOrderListVO> listOrders(OrderQueryRequest request) {
        // 1. 权限校验
        Long currentAdminId = requireAdmin();
        User admin = userMapper.selectById(currentAdminId);
        if (admin == null ||
                admin.getAccountStatus() != AccountStatus.ENABLED ||
                admin.getRole() != UserRole.ADMIN) {
            throw new RuntimeException("权限不足或账户已被禁用");
        }

        // 2. 分页参数
        int pageNum = Optional.ofNullable(request.getPageNum()).orElse(1);
        int pageSize = Optional.ofNullable(request.getPageSize()).orElse(20);
        if (pageNum < 1) pageNum = 1;
        if (pageSize < 1 || pageSize > 100) pageSize = 20;

        // 3. 构建订单查询条件
        LambdaQueryWrapper<RentalOrder> wrapper = new LambdaQueryWrapper<>();

        if (StringUtils.isNotBlank(request.getOrderNo())) {
            wrapper.like(RentalOrder::getOrderNo, request.getOrderNo());
        }
        if (request.getStatus() != null) {
            wrapper.eq(RentalOrder::getStatus, request.getStatus());
        }
        if (request.getStartDate() != null) {
            wrapper.ge(RentalOrder::getStartDate, request.getStartDate());
        }
        if (request.getEndDate() != null) {
            wrapper.le(RentalOrder::getEndDate, request.getEndDate());
        }

        // 按租用人姓名查询（realName 或 username）
        if (StringUtils.isNotBlank(request.getUserName())) {
            LambdaQueryWrapper<User> userQw = new LambdaQueryWrapper<>();
            userQw.like(User::getRealName, request.getUserName())
                    .or()
                    .like(User::getUsername, request.getUserName());
            List<Long> userIds = userMapper.selectList(userQw).stream()
                    .map(User::getId)
                    .collect(Collectors.toList());
            if (userIds.isEmpty()) {
                return new PageResult<>(pageNum, pageSize, 0, Collections.emptyList());
            }
            wrapper.in(RentalOrder::getUserId, userIds);
        }

        // 按商家名称查询（realName / username / shopName）
        if (StringUtils.isNotBlank(request.getMerchantName())) {
            LambdaQueryWrapper<User> merchantQw = new LambdaQueryWrapper<>();
            merchantQw.eq(User::getRole, UserRole.MERCHANT)
                    .and(qw -> qw.like(User::getRealName, request.getMerchantName())
                            .or().like(User::getUsername, request.getMerchantName())
                            .or().like(User::getShopName, request.getMerchantName()));
            List<Long> merchantIds = userMapper.selectList(merchantQw).stream()
                    .map(User::getId)
                    .collect(Collectors.toList());
            if (merchantIds.isEmpty()) {
                return new PageResult<>(pageNum, pageSize, 0, Collections.emptyList());
            }
            wrapper.in(RentalOrder::getMerchantId, merchantIds);
        }

        wrapper.orderByDesc(RentalOrder::getCreatedAt);

        // 4. 执行分页查询
        IPage<RentalOrder> orderPage = rentalOrderMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);

        // 5. 转换为 VO（完全匹配 AdminOrderListVO）
        List<AdminOrderListVO> voList = orderPage.getRecords().stream().map(order -> {
            User renter = userMapper.selectById(order.getUserId());
            User merchant = userMapper.selectById(order.getMerchantId());

            // 用户名脱敏：张三 → 张*
            String userName = "未知用户";
            if (renter != null) {
                String name = renter.getRealName();
                if (name == null || name.isBlank()) {
                    name = renter.getUsername();
                }
                if (name != null && !name.isBlank() && name.length() > 1) {
                    userName = name.charAt(0) + "*";
                } else if (name != null) {
                    userName = name + "*";
                }
            }

            // 商家名称
            String merchantName = "未知商户";
            if (merchant != null) {
                if (merchant.getShopName() != null && !merchant.getShopName().isBlank()) {
                    merchantName = merchant.getShopName();
                } else if (merchant.getRealName() != null && !merchant.getRealName().isBlank()) {
                    merchantName = merchant.getRealName();
                } else {
                    merchantName = merchant.getUsername();
                }
            }

            return new AdminOrderListVO()
                    .setOrderNo(order.getOrderNo())
                    .setUserName(userName)
                    .setMerchantName(merchantName)
                    .setStatusDesc(RentalOrderStatus.getDescByCode(order.getStatus()))
                    .setTotalAmount(order.getTotalAmount())
                    .setStartDate(order.getStartDate())
                    .setEndDate(order.getEndDate()) // 注意：VO 中 endDate 是 LocalDate，与 PO 一致
                    .setCreatedAt(order.getCreatedAt());
        }).collect(Collectors.toList());

        return new PageResult<>(pageNum, pageSize, orderPage.getTotal(), voList);
    }

    /**
     * 邮箱脱敏处理
     * 示例：user@example.com → u***@example.com
     */
    private String maskEmail(String email) {
        if (email == null || email.isEmpty()) return email;

        int atIndex = email.indexOf('@');
        if (atIndex <= 1) return email; // 无法脱敏的情况

        String prefix = email.substring(0, atIndex);
        String domain = email.substring(atIndex);

        if (prefix.length() > 4) {
            return prefix.charAt(0) + "***" + prefix.charAt(prefix.length() - 1) + domain;
        } else {
            return prefix.charAt(0) + "***" + domain;
        }
    }


    /**
     * 判断 targetId 是否是 sourceId 的后代（或自身）
     * 用于防止分类设置父级时形成循环引用
     *
     * @param sourceId 起始分类ID
     * @param targetId 目标父级ID
     * @return true 表示 targetId 是 sourceId 的自身或后代（即不能设为父）
     */
    private boolean isDescendantOrSelf(Long sourceId, Long targetId) {
        if (Objects.equals(sourceId, targetId)) {
            return true; // 不能设自己为父
        }
        if (targetId == null || targetId <= 0) {
            return false; // 顶级分类，无循环风险
        }

        // 递归向上查找 sourceId 的所有祖先，看是否包含 targetId
        Long current = sourceId;
        Set<Long> visited = new HashSet<>(); // 防止极端情况下的死循环（虽然数据库应无环）
        while (current != null && !Objects.equals(current, 0L)) {
            Category cat = categoryMapper.selectById(current);
            if (cat == null || cat.getDeleted() != 0) {
                break;
            }
            if (Objects.equals(cat.getParentId(), targetId)) {
                return true;
            }
            if (visited.contains(cat.getParentId())) {
                break; // 防环
            }
            visited.add(cat.getParentId());
            current = cat.getParentId();
        }
        return false;
    }

    /**
     * 校验当前用户为启用状态的管理员，并返回其 ID
     * @return 管理员用户 ID
     * @throws RuntimeException 权限不足或状态异常
     */
    private Long requireAdmin() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new RuntimeException("用户未登录");
        }

        Object principal = authentication.getPrincipal();
        if (!(principal instanceof LoginUser loginUser)) {
            throw new RuntimeException("无效的用户凭证");
        }

        Long userId = loginUser.getUserId();
        if (userId == null) {
            throw new RuntimeException("用户ID为空");
        }

        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        if (user.getRole() == null || user.getRole() != UserRole.ADMIN) {
            throw new RuntimeException("用户不是管理员");
        }

        if (user.getAccountStatus() == null || user.getAccountStatus() != AccountStatus.ENABLED) {
            throw new RuntimeException("账户已被禁用");
        }

        return userId;
    }


    /**
     * 审核通过后的业务逻辑（应在事务提交后执行）
     */
    private void executePostApprovalActions(User merchant, Long adminId) {
        try {
            // 1. 发送通知（邮件/短信/站内信）
            log.info("发送审核通过通知给商家: {}", merchant.getId());

            // 2. 初始化商家资源（示例）
            // createDefaultShop(merchant.getId());
            // assignMerchantNumber(merchant.getId());
            // enableMerchantApi(merchant.getId());

            // 3. 记录操作日志
            log.info("管理员 {} 审核通过商家 {}, IP: {}", adminId, merchant.getId(), getClientIp());

            // 4. 发布事件（供风控、BI等消费）
            // eventPublisher.publishEvent(new MerchantApprovedEvent(merchant.getId(), adminId));
        } catch (Exception e) {
            log.error("审核后处理异常，商家ID: {}", merchant.getId(), e);
            // 注意：此处不应抛出异常，避免影响主流程（因在事务提交后）
        }
    }

    /**
     * 审核拒绝后的业务逻辑（应在事务提交后执行）
     */
    private void executePostRejectActions(User merchant, Long adminId, String rejectionReason) {
        try {
            // 1. 发送拒绝通知（邮件/短信/站内信）
            log.info("已向商家发送注册被拒通知，商家ID: {}, 拒绝原因: {}", merchant.getId(), rejectionReason);

            // 2. 记录操作日志（无需初始化商家资源，因审核未通过）
            log.info("管理员拒绝商家注册申请，管理员ID: {}, 商家ID: {}, 拒绝原因: '{}', 客户端IP: {}",
                    adminId, merchant.getId(), rejectionReason, getClientIp());

            // 3. （可选）发布商家拒绝事件，供风控、BI 等下游系统消费
            // eventPublisher.publishEvent(new MerchantRejectedEvent(merchant.getId(), adminId, rejectionReason));

        } catch (Exception e) {
            log.error("执行商家拒绝后处理逻辑异常，商家ID: {}", merchant.getId(), e);
            // 注意：此处不抛出异常，避免影响主流程（因在事务提交后执行）
        }
    }

    /**
     * 获取客户端 IP（简化版，实际应从请求上下文获取）
     */
    private String getClientIp() {
        // 实际项目中应从 HttpServletRequest 获取
        return "0.0.0.0";
    }

    /**
     * 对手机号进行脱敏处理，保留前3位和后4位，中间4位替换为 ****。
     * <p>
     * 示例：
     * <ul>
     *   <li>{@code "13812345678"} → {@code "138****5678"}</li>
     *   <li>{@code null} → {@code null}</li>
     *   <li>{@code "12345"}（长度不足）→ {@code "12345"}</li>
     * </ul>
     *
     * @param phone 待脱敏的手机号字符串，可为 {@code null}
     * @return 脱敏后的手机号；若输入为 {@code null} 或长度小于11，则原样返回
     */
    private String maskPhone(String phone) {
        if (phone == null || phone.length() < 11) return phone;
        return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    }
}